home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
L' Effet Pommier 3
/
L'Effet Pommier - Volume 03.iso
/
Programmation
/
gray image 2.1
/
conv_filter.cc
< prev
next >
Wrap
Text File
|
1995-05-31
|
9KB
|
284 lines
// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Grayscale Image
*
* Perform convolutions, linear FIR filtrations
* of an image
*
* The present file implements several flavors of the linear FIR filtration,
* by rows, by columns, or a separable 2D filtration (which is filtration
* of rows followed by filtering the columns of an image). The file also
* contains the code for look-up table substitutions.
*
* The algorithm is simple, say for a 3-point FIR filter with coefficients
* [am1,a0,ap1], the pixels of a filtered image are computed as
* new_pixel[l] = am1*pixel[l-1] + a0*pixel[l] + ap1*pixel[l+1]
* For row-wise filtration, pixel[l] = image(i,l) and we repeat this
* for all rows i; In column-wise filtration, pixel[l] = image(l,j)
* and we make image.q_ncols() that many filtration (separate for
* each column of the image, that is).
* When filtering window sticks out of the image, we extrapolate
* the corresponding pixels. That is, we assume pixel[-1] = pixel[0]
* and pixel[lmax+1] = pixel[lmax]
*
* All filtration is done in-place!
*
* $Id: conv_filter.cc,v 2.0 1995/03/17 17:20:43 oleg Exp oleg $
*
************************************************************************
*/
#ifdef __GNUC__
#pragma implementation "filter.h"
#endif
#include "filter.h"
#include <minmax.h>
// Expand the templates right here...
template class ConvKernel3<CommonDenomOne>;
template class ConvKernel3<over_2_up>;
template class ConvKernel3<CommonDenom>;
/*
*------------------------------------------------------------------------
* Simple 3-point convolutions
*/
// 3-point row-wise convolutions
#define CONV3(Denom) \
\
IMAGE& FilterIt::conv_row(const ConvKernel3<Denom>& kernel) \
{ \
if( image.ncols < 2 ) \
image.info(), \
_error("we need at least 3 columns for a 3-point row convolution"); \
\
register GRAY_SIGNED * pp = (GRAY_SIGNED *)image.pixels; \
register GRAY_SIGNED prev_pixel; /* in the row */ \
\
for(GRAY_SIGNED * row_end = (GRAY_SIGNED *)image.pixels + image.ncols; \
row_end <= (GRAY_SIGNED *)image.pixels + image.npixels; \
row_end += image.ncols) \
{ \
prev_pixel = *pp; /* Assume pixel[-1]=pixel[0] */ \
\
while(pp < row_end-1) \
{ \
GRAY_SIGNED new_pixel = kernel.convolve(prev_pixel,pp[0],pp[1]); \
prev_pixel = pp[0]; \
*pp++ = new_pixel; \
} \
*pp++ = kernel.convolve(prev_pixel,pp[0],pp[0]); \
} \
assert( pp == (GRAY_SIGNED *)image.pixels + image.npixels ); \
return image; \
} \
\
/* 3-point col-wise convolution */ \
IMAGE& FilterIt::conv_col(const ConvKernel3<Denom>& kernel) \
{ \
if( image.nrows < 2 ) \
image.info(), \
_error("we need at least 3 rows for a 3-point col convolution"); \
\
register const int ncols = image.ncols; /* caching */ \
const int fl_jump = image.npixels - ncols; /* between 1. and last rows */\
register GRAY_SIGNED * pp = (GRAY_SIGNED *)image.pixels; \
register GRAY_SIGNED prev_pixel; /* in the col */ \
\
GRAY_SIGNED * last_row = (GRAY_SIGNED *)image.pixels + fl_jump; \
for(; last_row < (GRAY_SIGNED *)image.pixels + image.npixels; \
last_row++) \
{ \
prev_pixel = *pp; /* Assume pixel[-1]=pixel[0] */ \
\
for(; pp < last_row; pp += ncols) \
{ \
GRAY_SIGNED new_pixel = kernel.convolve(prev_pixel,pp[0],pp[ncols]); \
prev_pixel = pp[0]; \
pp[0] = new_pixel; \
} \
pp[0] = kernel.convolve(prev_pixel,pp[0],pp[0]); \
pp -= fl_jump-1; /* Jump over the next col */ \
} \
assert( pp == last_row - fl_jump ); \
return image; \
} \
CONV3(CommonDenomOne)
CONV3(over_2_up)
CONV3(CommonDenom)
#undef CONV3
//------------------------------------------------------------------------
// Routing filtration module
// pick up and execute the most efficient method given input parameters
//
// I wish I could use templates, but theu suck! I can't declare templates
// inside classes, w/o making an entire type templated
// I can't help but resort to a good old c preprocessor
#define CONV_ROUTER(Denom) \
\
IMAGE& FilterIt::conv(const ConvKernel3<Denom>& kernel,const Direction how) \
{ \
image.is_valid(); \
\
switch(how) \
{ \
case RowsAndColumns: \
return conv_row(kernel), conv_col(kernel); \
case Columns: \
return conv_col(kernel); \
case Rows: \
return conv_row(kernel); \
} \
_error("There is no way we can reach here..."); \
return image; /* just to make the compiler happy */\
} \
CONV_ROUTER(CommonDenomOne)
CONV_ROUTER(over_2_up)
CONV_ROUTER(CommonDenom)
#undef CONV_ROUTER
/*
*------------------------------------------------------------------------
* Table Look-ups
*/
// Allocate an empty look-up table
void LookupT::allocate(const GRAY _min_val, const GRAY _max_val)
{
assure(_max_val >= _min_val, "bad range allocating the LookUp table");
max_val = _max_val;
min_val = _min_val;
no_entries = max_val - min_val + 1;
if( no_entries > Preallocated_no )
table = new GRAY[no_entries];
else // Use storage preallocated within
table = preallocated; // the object whenever possible
}
// Copy constructor
LookupT::LookupT(const LookupT& another)
{
allocate(another.min_val,another.max_val);
*this = another;
}
// It's OK to assign one LookupT to another
// But they have to be similar
LookupT& LookupT::operator= (const LookupT& another)
{
if( min_val != another.min_val || max_val != another.max_val )
_error("Can't assign [%d,%d]-range look-up table to [%d,%d] one",
another.min_val, another.max_val, min_val, max_val );
assert( no_entries == another.no_entries );
memcpy(table,another.table,sizeof(table[0])*no_entries);
return *this;
}
// Destructor
LookupT::~LookupT(void)
{
assert( table != 0 && no_entries > 0 && max_val >= min_val );
if( table != preallocated )
delete [] table;
table = 0;
}
// Build a table for an identical pixel
// mapping for a specified depth image
LookupT::LookupT(const Identical id)
{
assure(id.depth > 0 && id.depth < 14,
"a fishy depth to build a lookup table for");
allocate(0,(1<<id.depth)-1);
for(register GRAY i=min_val; i<=max_val; i++)
table[i-min_val] = i;
}
// Build a table to map just one
// particular pixel
LookupT::LookupT(const MapTo map_one)
{
allocate(map_one.from,map_one.from);
(*this)[map_one.from] = map_one.to;
}
// Build a table to reduce image depth from
// one value to another (that is, build a
// stripy table)
LookupT::LookupT(const DownGRAY downgray)
{
assure(downgray.depth_from > 0 && downgray.depth_from < 14 &&
downgray.depth_from >= downgray.depth_to,
"a fishy depths to build a downgray lookup table for");
allocate(0,(1<<downgray.depth_from)-1);
const card downgr_factor = 1<<(downgray.depth_from-downgray.depth_to);
register GRAY * tp = table;
register GRAY val;
for(val=0; tp < table + no_entries; val++)
for(register int i=0; i<downgr_factor; i++)
*tp++ = val;
assert( tp == table + no_entries && val == 1<<downgray.depth_to );
}
// Convolve two Lookup tables
LookupT& LookupT::apply(const LookupT& another,
const FringeHandling fringe_handling)
{
register GRAY_SIGNED * tp;
if( fringe_handling == CoerceFringes )
for(tp=(GRAY_SIGNED *)table; tp < (GRAY_SIGNED *)table + no_entries; tp++)
*tp = another[min(max(*tp,(GRAY_SIGNED)another.min_val),
(GRAY_SIGNED)another.max_val)];
else // Leave fringes alone
for(tp=(GRAY_SIGNED *)table; tp < (GRAY_SIGNED *)table + no_entries; tp++)
if( *tp >= another.min_val && *tp <= another.max_val )
*tp = another[*tp];
return *this;
}
// Translate image pixels according to
// the look-up table
IMAGE& FilterIt::translate(const LookupT& lookupt,
const LookupT::FringeHandling fringe_handling)
{
register GRAY_SIGNED * pp = (GRAY_SIGNED *)image.pixels;
if( fringe_handling == LookupT::CoerceFringes )
while( pp < (GRAY_SIGNED *)image.pixels + image.npixels )
{
int offset = *pp - lookupt.min_val;
if( offset < 0 )
offset = 0;
else if( offset >= lookupt.no_entries )
offset = lookupt.no_entries - 1;
*pp++ = lookupt.table[offset];
}
else // Leave fringes alone
for(; pp < (GRAY_SIGNED *)image.pixels + image.npixels; pp++)
{
int offset = *pp - lookupt.min_val;
if( offset >= 0 && offset < lookupt.no_entries )
*pp = lookupt.table[offset];
}
return image;
}